package com.itheima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie;
import com.itheima.admin.mapper.AdChannelMapper;
import com.itheima.admin.mapper.AdSensitiveMapper;
import com.itheima.admin.pojo.AdChannel;
import com.itheima.admin.service.WemediaNewsAutoScanService;
import com.itheima.article.dto.ArticleInfoDto;
import com.itheima.article.feign.ApArticleFeign;
import com.itheima.article.pojo.ApArticle;
import com.itheima.article.pojo.ApArticleConfig;
import com.itheima.article.pojo.ApArticleContent;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.util.SensitiveWordUtil;
import com.itheima.common.util.ali.AliGreenScanner;
import com.itheima.common.vo.ResultVo;
import com.itheima.dfs.feign.DfsFeign;
import com.itheima.wemedia.pojo.WmNews;
import com.itheima.wemedia.dto.WmNewsContentNodeDto;
import com.itheima.wemedia.feign.WmNewsFeign;
import com.itheima.wemedia.vo.WmNewsVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description 说明
 * @package com.itheima.admin.service.impl
 */
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {

    @Autowired
    private WmNewsFeign wmNewsFeign;

    @Autowired
    private DfsFeign dfsFeign;

    @Autowired
    private ApArticleFeign apArticleFeign;

    @Autowired
    private AdSensitiveMapper adSensitiveMapper;

    @Autowired
    private AdChannelMapper adChannelMapper;

    @Autowired
    private AliGreenScanner aliGreenScanner;


    /**
     * 自媒体文章 - 自动审核
     *
     * @param wmNewsId
     */
    @Override
    public void autoScanWemediaNewsById(Long wmNewsId, String uuid) {
        //1. 通过文章id远程调用自媒体查询文章信息
        ResultVo<WmNews> getWmNewsResult = wmNewsFeign.getWmNewsById(wmNewsId);
        log.debug("远程调用自媒体查询文章信息结果: {},{}",uuid,getWmNewsResult);
        if(!getWmNewsResult.isSuccess()){
            log.error("远程调用自媒体查询文章失败:{},{},{}", uuid,wmNewsId,getWmNewsResult.getErrorMessage());
            throw new LeadNewsException("远程调用自媒体查询文章失败!");
        }
        // 得到文章信息
        WmNews wmNews = getWmNewsResult.getData();
        if(null == wmNews){
            log.error("远程调用自媒体查询文章为空:{},{}", uuid,wmNewsId);
            return;
        }
        // 状态判断
        if (BusinessConstants.WmNewsConstants.STATUS_SUBMIT != wmNews.getStatus()) {
            log.error("文章状态为:{},不是待审核，此次不处理,{}", wmNews.getStatus(),uuid);
            return;
        }
        //提取文本 标题+内容
        String text = getTextFromWmNews(wmNews);
        //做敏感词检测
        boolean flag = sensitiveWordCheck(text);
        // 更新状态的数据
        WmNews updatePojo = new WmNews();
        updatePojo.setId(wmNewsId);
        if(!flag){
            // 没有敏感词 敏感词检测通过
            Map<String, String> scanResult = null;
            try {
                scanResult = aliGreenScanner.greenTextScan(text);
                if (null == scanResult && "pass".equals(scanResult.get("suggestion"))) {
                    // 检测图片
                    scanResult = null;// 重置反垃圾结果，以图片检测结果为准
                    // 提取文章中所有图片的url
                    Set<String> urls = getImagesFromWmNews(wmNews);
                    // 下载图片, 远程调用dfs
                    ResultVo<List<byte[]>> downloadResult = dfsFeign.downloadFiles(urls);
                    // 检查下载的结果
                    if (downloadResult.isSuccess()) {
                        List<byte[]> imageDataList = downloadResult.getData();
                        // 阿里图片反垃圾检测
                        scanResult = aliGreenScanner.imageScan(imageDataList);
                    }else{
                        log.error("远程调用文件微服下载图片失败:{}",uuid);
                    }
                }
            } catch (Exception e) {
                log.error("调用阿里文本反垃圾失败:{}", uuid,e);// 得人工处理
            }
            //统一处理 阿里(文本、图片)的结果
            if(null == scanResult){
                // 最好加上uuid,方法运维|审核人员 追踪日志
                updatePojo.setReason("调用阿里反垃圾失败!");
                // 人工审核
                updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL);
            }else{
                String suggestion = scanResult.get("suggestion");
                switch (suggestion){
                    // 审核通过
                    case "pass": updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_PASS);break;
                    // 需要人工审核
                    case "review": updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL);break;
                    // 审核不通过
                    case "block":
                        updatePojo.setReason("调用阿里反垃圾 审核不通过!");
                        updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
                        break;
                }
            }
        }else{
            // 更新 状态为审核失败
            updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
            updatePojo.setReason("文章包含敏感词");
        }
        // 远程调用 更新文章状态
        ResultVo updateResult = wmNewsFeign.updateWmNews(updatePojo);
        if(!updateResult.isSuccess()){
            log.error("远程调用自媒体更新文章状态失败!{}", uuid);
            throw new LeadNewsException("远程调用自媒体更新文章状态失败!");
        }
    }


    /**
     *  同步文章数据
     * @param wmNewsVo
     * @return
     */
    @Override
    public boolean syncArticleInfo(WmNewsVo wmNewsVo) {
        //1. 构建articleInfoDto
        ArticleInfoDto dto = new ArticleInfoDto();
        //2. 构建文章信息apArticle
        ApArticle apArticle = buildApArticle(wmNewsVo);
        dto.setApArticle(apArticle);

        //3. 构建文章内容对象apArticleContent
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setContent(wmNewsVo.getContent());
        dto.setApArticleContent(apArticleContent);
        //4. 构建文章配置对象apArticleConfig
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setIsDown(0);// 初始化为非下架
        apArticleConfig.setIsDelete(0);// 0：没被删除
        apArticleConfig.setIsForward(1);
        apArticleConfig.setIsComment(1);
        dto.setApArticleConfig(apArticleConfig);
        //5. 自媒体用户
        dto.setApUserId(wmNewsVo.getUserId());
        // app用户id 作者
        dto.setApUserId(wmNewsVo.getUserId());
        // 自媒体文章id(更新状态)
        dto.setWmNewsId(wmNewsVo.getId());
        //6. 远程调用文章微服务保存文章信息
        ResultVo resultVo = apArticleFeign.saveArticleInfo(dto);
        //7. 解析返回结果
        return resultVo.isSuccess();
    }

    private ApArticle buildApArticle(WmNewsVo wmNewsVo) {
        ApArticle apArticle = new ApArticle();
        // 复制属性
        BeanUtils.copyProperties(wmNewsVo, apArticle);
        //优化修改文章时删除已发布的旧文章
        apArticle.setId(wmNewsVo.getArticleId());
        // 作者id
        apArticle.setAuthorId(wmNewsVo.getUserId());
        // 作者名称
        apArticle.setAuthorName(wmNewsVo.getAuthorName());
        // 频道名称
        Integer channelId = wmNewsVo.getChannelId();
        AdChannel adChannel = adChannelMapper.selectById(channelId);
        apArticle.setChannelName(adChannel.getName());
        // 布局
        apArticle.setLayout(wmNewsVo.getType());
        apArticle.setFlag(0);//普通文章
        apArticle.setLikes(0);
        apArticle.setCollection(0);
        apArticle.setComment(0);
        apArticle.setViews(0);
        apArticle.setProvinceId(0);
        apArticle.setCityId(0);
        apArticle.setCountyId(0);
        apArticle.setSyncStatus(0);//0:未同步给es
        apArticle.setOrigin(1);//1:自媒体，2：爬虫
        return apArticle;
    }

    /**
     * 提取文章中所有图片的url
     * @param wmNews
     * @return
     */
    private Set<String> getImagesFromWmNews(WmNews wmNews) {
        Set<String> set = new HashSet<>();
        //封面处理
        String covers = wmNews.getImages();
        if(!StringUtils.isEmpty(covers)){
            // 不为空，有封面
            Collections.addAll(set, covers.split(","));
        }
        // 提取内容中的图片
        String content = wmNews.getContent();
        if(!StringUtils.isEmpty(content)){
            List<WmNewsContentNodeDto> contentNodes = JSON.parseArray(content, WmNewsContentNodeDto.class);
            // 过滤且只需要图片
            Set<String> imageSet = contentNodes.stream().filter(n -> BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(n.getType()))
                    .map(WmNewsContentNodeDto::getValue).collect(Collectors.toSet());
            set.addAll(imageSet);
        }
        return set;
    }

    /**
     * 做敏感词检测
     * @param text
     * @return
     */
    private boolean sensitiveWordCheck(String text) {
        //查询所有的敏感词, 数据库
        List<String> sensitiveWords = adSensitiveMapper.selectAllSensitives();
        // 提取敏感词内容
        // 构建DFA的util
        SensitiveWordUtil.initMap(sensitiveWords);
        /*敏感词v1.0版本        // 使用DFA检测
        Map<String, Integer> result = SensitiveWordUtil.matchWords(text);
        // 返回结果
        return result.size()>0;*/
        //敏感词优化v2.0
        Map<String, String> map = sensitiveWords.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
        // 双数组字典树
        AhoCorasickDoubleArrayTrie<String> acdat = new AhoCorasickDoubleArrayTrie<String>();
        acdat.build(map);
        AhoCorasickDoubleArrayTrie.Hit<String> first = acdat.findFirst(text);
        // 返回结果
        return first!=null;
    }

    /**
     * 提取文本
     * @param wmNews
     * @return
     */
    private String getTextFromWmNews(WmNews wmNews) {
        // 标题
        String content = wmNews.getTitle();
        // 获取内容
        String jsonString = wmNews.getContent();
        if(!StringUtils.isEmpty(jsonString)) {
            List<WmNewsContentNodeDto> contentNodes = JSON.parseArray(jsonString, WmNewsContentNodeDto.class);
            // 过滤且只需要文本, 不为图片即为文本, 并文本内容以句号分割
            String contentString = contentNodes.stream().filter(n -> !BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(n.getType()))
                    .map(WmNewsContentNodeDto::getValue).collect(Collectors.joining("。"));
            content += "。" + contentString;
        }
        return content;
    }
}
